#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <gl\glaux.h>
#include <iostream>
#include <mmsystem.h>
#include "image.h"
using namespace std;

extern "C" {
	FILE* _iob = NULL;
}

TGAImage *loadBMP(const char *fileName)
{
	AUX_RGBImageRec *bitmap = NULL;
	FILE *file = NULL;

	if ((fopen_s(&file, fileName, "rb")) != 0)
	{
		// TODO: add an error message
		return NULL;
	}

	bitmap = auxDIBImageLoad(fileName);
	TGAImage *image = (TGAImage*)malloc(sizeof(TGAImage));
	image->channels = 3;
	image->sizeX = bitmap->sizeX;
	image->sizeY = bitmap->sizeY;
	image->data = bitmap->data;

	free(bitmap);
	
	return image;
}

TGAImage *loadTGA(const char *fileName)
{
	TGAImage *imageData = NULL;			// This stores our important image data
	WORD width = 0, height = 0;			// The dimensions of the image
	byte length = 0;					// The length in bytes to the pixels
	byte imageType = 0;					// The image type (RLE, RGB, Alpha...)
	byte bits = 0;						// The bits per pixel for the image (16, 24, 32)
	FILE *file = NULL;					// The file pointer
	int channels = 0;					// The channels of the image (3 = RGA : 4 = RGBA)
	int stride = 0;						// The stride (channels * width)
	int i = 0;							// A counter

	if((fopen_s(&file, fileName, "rb")) != 0) 
	{
		// TODO: add an error message
		return NULL;
	}
		
	imageData = (TGAImage*)malloc(sizeof(TGAImage));

	fread(&length, sizeof(byte), 1, file);
	fseek(file,1,SEEK_CUR); 
	fread(&imageType, sizeof(byte), 1, file);
	fseek(file, 9, SEEK_CUR); 
	fread(&width,  sizeof(WORD), 1, file);
	fread(&height, sizeof(WORD), 1, file);
	fread(&bits,   sizeof(byte), 1, file);
	fseek(file, length + 1, SEEK_CUR); 

	if(imageType != TGA_RLE)
	{
		if(bits == 24 || bits == 32)
		{
			channels = bits / 8;
			stride = channels * width;
			imageData->data = ((unsigned char*)malloc(sizeof(unsigned char)*stride*height));

			for(int y = 0; y < height; y++)
			{
				// Store a pointer to the current line of pixels
				unsigned char *line = &(imageData->data[stride * y]);
				fread(line, stride, 1, file);
			
				// Go through all of the pixels and swap the B and R values since TGA
				// files are stored as BGR instead of RGB
				for(i = 0; i < stride; i += channels)
				{
					int temp     = line[i];
					line[i]     = line[i + 2];
					line[i + 2] = temp;
				}
			}
		}
		else if(bits == 16)
		{
			unsigned short pixels = 0;
			int r = 0, g = 0, b = 0;

			// Since we convert 16-bit images to 24 bit, we hardcode the channels to 3.
			// We then calculate the stride and allocate memory for the pixels.
			channels = 3;
			stride = channels * width;
			imageData->data = ((unsigned char*)malloc(sizeof(unsigned char)*stride*height));

			for(int i = 0; i < width*height; i++)
			{
				// Read in the current pixel
				fread(&pixels, sizeof(unsigned short), 1, file);
				
				// Convert the 16-bit pixel into an RGB
				b = (pixels & 0x1f) << 3;
				g = ((pixels >> 5) & 0x1f) << 3;
				r = ((pixels >> 10) & 0x1f) << 3;
				
				// This essentially assigns the color to our array and swaps the
				// B and R values at the same time.
				imageData->data[i * 3 + 0] = r;
				imageData->data[i * 3 + 1] = g;
				imageData->data[i * 3 + 2] = b;
			}
		}	
		else
			return NULL;
	}
	// Else, it must be Run-Length Encoded (RLE)
	else
	{
		byte rleID = 0;
		int colorsRead = 0;
		channels = bits / 8;
		stride = channels * width;

		imageData->data = ((unsigned char*)malloc(sizeof(unsigned char)*stride*height));
		byte *colors = ((byte*)malloc(sizeof(byte)*channels));

		while(i < width*height)
		{
			// Read in the current color count + 1
			fread(&rleID, sizeof(byte), 1, file);
			
			// Check if we don't have an encoded string of colors
			if(rleID < 128)
			{
				rleID++;

				// Go through and read all the unique colors found
				while(rleID)
				{
					// Read in the current color
					fread(colors, sizeof(byte) * channels, 1, file);
					imageData->data[colorsRead + 0] = colors[2];
					imageData->data[colorsRead + 1] = colors[1];
					imageData->data[colorsRead + 2] = colors[0];

					// If we have a 4 channel 32-bit image, assign one more for the alpha
					if(bits == 32)
						imageData->data[colorsRead + 3] = colors[3];

					// Increase the current pixels read, decrease the amount
					// of pixels left, and increase the starting index for the next pixel.
					i++;
					rleID--;
					colorsRead += channels;
				}
			}
			// Else, let's read in a string of the same character
			else
			{
				// Minus the 128 ID + 1 (127) to get the color count that needs to be read
				rleID -= 127;

				// Read in the current color, which is the same for a while
				fread(colors, sizeof(byte) * channels, 1, file);

				// Go and read as many pixels as are the same
				while(rleID)
				{
					// Assign the current pixel to the current index in our pixel array
					imageData->data[colorsRead + 0] = colors[2];
					imageData->data[colorsRead + 1] = colors[1];
					imageData->data[colorsRead + 2] = colors[0];

					// If we have a 4 channel 32-bit image, assign one more for the alpha
					if(bits == 32)
						imageData->data[colorsRead + 3] = colors[3];

					// Increase the current pixels read, decrease the amount
					// of pixels left, and increase the starting index for the next pixel.
					i++;
					rleID--;
					colorsRead += channels;
				}
				
			}
				
		}
	}

	fclose(file);

	imageData->channels = channels;
	imageData->sizeX    = width;
	imageData->sizeY    = height;

	return imageData;
}

TGAImage *loadJPG(const char *fileName)
{
	struct jpeg_decompress_struct info;
	TGAImage *imageData = NULL;
	FILE *file;
		
	// Open a file pointer to the jpeg file and check if it was found and opened 
	if((fopen_s(&file, fileName, "rb")) != 0) 
	{
		// TODO: add an error message
		return NULL;
	}
	
	// Create an error handler
	jpeg_error_mgr jerr;

	// Have our compression info object point to the error handler address
	info.err = jpeg_std_error(&jerr);
	
	// Initialize the decompression object
	jpeg_create_decompress(&info);
	
	// Specify the data source
	jpeg_stdio_src(&info, file);
	
	imageData = (TGAImage*)malloc(sizeof(TGAImage));
	decodeJPG(&info, imageData);
	
	// This releases all the stored memory for reading and decoding the jpeg
	jpeg_destroy_decompress(&info);

	fclose(file);
	return imageData;
}

void decodeJPG(jpeg_decompress_struct *info, TGAImage *imageData)
{
	jpeg_read_header(info, TRUE);
	
	// Start to decompress the jpeg file with our compression info
	jpeg_start_decompress(info);

	imageData->channels = info->num_components;
	imageData->sizeX    = info->image_width;
	imageData->sizeY    = info->image_height;

	int rowSpan = info->image_width * info->num_components;
	imageData->data = ((unsigned char*)malloc(sizeof(unsigned char)*rowSpan*imageData->sizeY));
	unsigned char** rowPtr = new unsigned char*[imageData->sizeY];

	for (int i = 0; i < imageData->sizeY; i++)
		rowPtr[i] = &(imageData->data[i * rowSpan]);

	int rowsRead = 0;
	while (info->output_scanline < info->output_height) 
	{
		rowsRead += jpeg_read_scanlines(info, &rowPtr[rowsRead], info->output_height - rowsRead);
	}
	
	delete [] rowPtr;

	jpeg_finish_decompress(info);
}